/******************************************************************************* * Copyright (c) 2008, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.tools.builders; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaModelMarker; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.BuildContext; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.CompilationParticipant; import org.eclipse.jdt.core.compiler.batch.BatchCompiler; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.swt.tools.Activator; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; public class Check64CompilationParticipant extends CompilationParticipant { HashSet<String> sources; static final char[] INT_LONG = "int /*long*/".toCharArray(); static final char[] INT_LONG_ARRAY = "int[] /*long[]*/".toCharArray(); static final char[] FLOAT_DOUBLE = "float /*double*/".toCharArray(); static final char[] FLOAT_DOUBLE_ARRAY = "float[] /*double[]*/".toCharArray(); static final char[] LONG_INT = "long /*int*/".toCharArray(); static final char[] LONG_INT_ARRAY = "long[] /*int[]*/".toCharArray(); static final char[] DOUBLE_FLOAT = "double /*float*/".toCharArray(); static final char[] DOUBLE_FLOAT_ARRAY = "double[] /*float[]*/".toCharArray(); static final String buildDir = "/.build64/"; static final String pluginDir = "/org.eclipse.swt/"; static final String plugin = "org.eclipse.swt"; static final String SOURCE_ID = "JNI"; static final String CHECK_64_ENABLED = Activator.PLUGIN_ID + "CHECK_64_ENABLED"; static String loadFile (String file) { if (file == null) return null; try { FileReader fr = new FileReader(file); BufferedReader br = new BufferedReader(fr); StringBuffer str = new StringBuffer(); char[] buffer = new char[1024]; int read; while ((read = br.read(buffer)) != -1) { str.append(buffer, 0, read); } fr.close(); return str.toString(); } catch (IOException e) { throw new RuntimeException("File not found:" + file, e); } } void build(IJavaProject project, String root) throws CoreException { PrintWriter writer = null; try { StringBuffer sourcePath = new StringBuffer(), cp = new StringBuffer(); IClasspathEntry[] entries = project.getResolvedClasspath(true); for (int i = 0; i < entries.length; i++) { IClasspathEntry entry = entries[i]; String path = entry.getPath().toPortableString(); if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { if (path.startsWith(pluginDir)) { if (sourcePath.length() > 0) sourcePath.append(File.pathSeparatorChar); String dir = root + path.substring(pluginDir.length()); sourcePath.append(dir); } } else { if (cp.length() > 0) cp.append(File.pathSeparator); cp.append(path); } } String bin = root + "/bin"; if (cp.length() > 0) cp.append(File.pathSeparator); cp.append(bin); ArrayList<String> args = new ArrayList<String>(); args.addAll(Arrays.asList(new String[]{ "-nowarn", "-1.5", // "-verbose", "-d", bin, "-cp", cp.toString(), "-log", root + "/log.xml", "-sourcepath", sourcePath.toString(), })); args.addAll(sources); writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(root + "/out.txt"))); BatchCompiler.compile(args.toArray(new String[args.size()]), writer, writer, null); writer.close(); writer = null; project.getProject().findMember(new Path(buildDir)).refreshLocal(IResource.DEPTH_INFINITE, null); } catch (Exception e) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Problem building 64-bit code", e)); } finally { if (writer != null) writer.close(); } } void create(IContainer file) throws CoreException { if (file.exists()) return; switch (file.getType()) { case IResource.FOLDER: create(file.getParent()); ((IFolder)file).create(true, true, null); } } IResource getResourceWithoutErrors(IProject proj, String path, boolean deleteJNI) throws CoreException { path = path.replaceAll(buildDir, "/"); String projPath = proj.getLocation().toPortableString(); if (path.startsWith(projPath)) { path = path.substring(projPath.length()); } IResource resource = proj.findMember(new Path(path)); boolean hasProblems = false; IMarker[] markers = resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); for (int m = 0; m < markers.length; m++) { IMarker marker = markers[m]; if (SOURCE_ID.equals(marker.getAttribute(IMarker.SOURCE_ID))) { if (deleteJNI) marker.delete(); } else { Object severity = marker.getAttribute(IMarker.SEVERITY); hasProblems |= severity != null && ((Integer)severity).intValue() == IMarker.SEVERITY_ERROR; } } return hasProblems ? null : resource; } void createProblem(IResource resource, String message, int start, int end) throws CoreException { IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER); int severity = IMarker.SEVERITY_ERROR; marker.setAttributes( new String[] {IMarker.MESSAGE, IMarker.SEVERITY, IMarker.CHAR_START, IMarker.CHAR_END, IMarker.SOURCE_ID}, new Object[] {"[32/64] " + message, new Integer(severity), new Integer(start), new Integer(end), SOURCE_ID}); } void createProblems(IJavaProject project, String root) throws CoreException { try { InputStream is = new BufferedInputStream(new FileInputStream(root + "/log.xml")); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(is)); is.close(); IProject proj = project.getProject(); NodeList sources = doc.getDocumentElement().getElementsByTagName("sources"); for (int i = 0; i < sources.getLength(); i++) { NodeList src = ((Element)sources.item(i)).getElementsByTagName("source"); for (int j = 0; j < src.getLength(); j++) { Element source = (Element)src.item(j); String path = source.getAttribute("path").replace('\\', '/'); IResource resource = getResourceWithoutErrors(proj, path, true); if (resource != null) { NodeList problems = source.getElementsByTagName("problems"); for (int k = 0; k < problems.getLength(); k++) { NodeList problem = ((Element)problems.item(k)).getElementsByTagName("problem"); for (int l = 0; l < problem.getLength(); l++) { Element node = (Element)problem.item(l); if (resource != null) { int start = Integer.parseInt(node.getAttribute("charStart")); int end = Integer.parseInt(node.getAttribute("charEnd")); String message = ((Element)node.getElementsByTagName("message").item(0)).getAttribute("value"); createProblem(resource, message, start, end); } } } } } } } catch (Exception e) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Problem creating 64-bit problems", e)); } } String resolvePath(String sourcePath, String simpleName) { String basePath = sourcePath.substring(0, sourcePath.lastIndexOf("/")); File file = new File(basePath + "/" + simpleName + ".java"); if (file.exists()) { return file.getAbsolutePath(); } // System.out.println("failed=" + simpleName + " " + sourcePath); return null; } TypeDeclaration loadType(HashMap<String, TypeDeclaration> cache, String path) { if (path == null) return null; Object value = cache.get(path); if (value != null) return (TypeDeclaration)value; ASTParser parser = ASTParser.newParser(AST.JLS4); parser.setSource(loadFile(path).toCharArray()); CompilationUnit unit = (CompilationUnit)parser.createAST(null); TypeDeclaration type = (TypeDeclaration)unit.types().get(0); cache.put(path, type); return type; } boolean is64Type(String type) { return type.equals("int") || type.equals("long") || type.equals("float") || type.equals("double") || type.equals("int[]") || type.equals("long[]") || type.equals("float[]") || type.equals("double[]"); } void createBadOverwrittenMethodProblems(IJavaProject project, String root) throws CoreException { if (sources == null) return; IProject proj = project.getProject(); HashMap<String, TypeDeclaration> cache = new HashMap<String, TypeDeclaration>(); for (Iterator<String> iterator = sources.iterator(); iterator.hasNext();) { String path = iterator.next(); IResource resource = getResourceWithoutErrors(proj, path, false); if (resource == null) continue; TypeDeclaration type = loadType(cache, path); MethodDeclaration[] methods = type.getMethods(); List<TypeDeclaration> superclasses = new ArrayList<TypeDeclaration>(); TypeDeclaration temp = type; while (true) { Type supertype = temp.getSuperclassType(); if (supertype == null) break; TypeDeclaration stype = loadType(cache, resolvePath(path, supertype.toString())); if (stype == null) break; superclasses.add(stype); temp = stype; } for (int i = 0; i < methods.length; i++) { MethodDeclaration method = methods[i]; for (Iterator<TypeDeclaration> iterator2 = superclasses.iterator(); iterator2.hasNext();) { TypeDeclaration supertype = iterator2.next(); MethodDeclaration[] supermethods = supertype.getMethods(); for (int j = 0; j < supermethods.length; j++) { MethodDeclaration supermethod = supermethods[j]; if (method.getName().getIdentifier().equals(supermethod.getName().getIdentifier()) && method.parameters().size() == supermethod.parameters().size()) { List<SingleVariableDeclaration> mParams = method.parameters(); List<SingleVariableDeclaration> sParams = supermethod.parameters(); for (int k=0; k<sParams.size(); k++) { SingleVariableDeclaration p1 = mParams.get(k); SingleVariableDeclaration p2 = sParams.get(k); String r1 = p1.getType().toString(); String r2 = p2.getType().toString(); if (is64Type(r1) && is64Type(r2)) { if (!r1.equals(r2) && p1.getName().getIdentifier().equals(p2.getName().getIdentifier())) { String message = "\"" + p1.getName().getIdentifier() + "\" parameter type does not match super declaration"; createProblem(resource, message, p1.getStartPosition(), p1.getStartPosition() + p1.toString().length()); } } } } } } } } } boolean replace(char[] source, char[] src, char[] dest) { boolean changed = false; int start = 0; while (start < source.length) { int index = CharOperation.indexOf(src, source, true, start); if (index == -1) break; changed |= true; System.arraycopy(dest, 0, source, index, dest.length); start = index + 1; } return changed; } boolean replace(char[] source) { boolean changed = false; changed |= replace(source, LONG_INT, INT_LONG); changed |= replace(source, LONG_INT_ARRAY, INT_LONG_ARRAY); changed |= replace(source, DOUBLE_FLOAT, FLOAT_DOUBLE); changed |= replace(source, DOUBLE_FLOAT_ARRAY, FLOAT_DOUBLE_ARRAY); if (!changed) { changed |= replace(source, INT_LONG, LONG_INT); changed |= replace(source, INT_LONG_ARRAY, LONG_INT_ARRAY); changed |= replace(source, FLOAT_DOUBLE, DOUBLE_FLOAT); changed |= replace(source, FLOAT_DOUBLE_ARRAY, DOUBLE_FLOAT_ARRAY); } return changed; } public static boolean getEnabled() { return Activator.getDefault().getPreferenceStore().getBoolean(CHECK_64_ENABLED); } public static void setEnabled(boolean enabled) { Activator.getDefault().getPreferenceStore().setValue(CHECK_64_ENABLED, enabled); } boolean is64bit(IJavaProject project) { try { IClasspathEntry[] entries = project.getResolvedClasspath(true); for (int i = 0; i < entries.length; i++) { IClasspathEntry entry = entries[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { String path = entry.getPath().toPortableString(); if (path.equals(pluginDir + "Eclipse SWT PI/win32") || path.equals(pluginDir + "Eclipse SWT PI/cocoa") || path.equals(pluginDir + "Eclipse SWT PI/gtk") ) { return true; } } } } catch (JavaModelException e) {} return false; } @Override public void buildFinished(IJavaProject project) { try { if (sources == null) return; if (!getEnabled() || !is64bit(project)) return; // long time = System.currentTimeMillis(); String root = project.getProject().getLocation().toPortableString() + buildDir; build(project, root); createProblems(project, root); createBadOverwrittenMethodProblems(project, root); sources = null; // System.out.println("compiling time=" + (System.currentTimeMillis() - time)); } catch (Exception e) { e.printStackTrace(); } } @Override public void buildStarting(BuildContext[] files, boolean isBatch) { if (sources == null) sources = new HashSet<String>(); // long time = System.currentTimeMillis(); for (int i = 0; i < files.length; i++) { BuildContext context = files[i]; IFile file = context.getFile(); IProject project = file.getProject(); Path path = new Path(buildDir + file.getProjectRelativePath().toPortableString()); IFile newFile = project.getFile(path); sources.add(newFile.getLocation().toPortableString()); try { if (newFile.exists()) { newFile.delete(true, null); } create(newFile.getParent()); char[] source = context.getContents(); replace(source); newFile.create(new ByteArrayInputStream(new String(source).getBytes()), true, null); } catch (CoreException e) { e.printStackTrace(); } } // System.out.println("copying time=" + (System.currentTimeMillis() - time)); } @Override public void cleanStarting(IJavaProject project) { if (!isActive(project)) return; sources = null; IResource resource = project.getProject().findMember(new Path(buildDir)); if (resource != null) { try { resource.delete(true, null); } catch (CoreException e) { e.printStackTrace(); } } } @Override public boolean isActive(IJavaProject project) { if (project.getProject().getName().equals(plugin)) { return true; } return super.isActive(project); } }